﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using iTFF.WPF.CP;
using System.IO;
using System.Xml.Serialization;
using System.Xml;
using iTFF.WPF.Resources.Languages;

namespace iTFF.WPF.Model.Lyrics.LocalProviders
{
    class FileLocalProvider : ILyricsLocalProvider
    {
        #region Fields

        iTunesControlPanel _itcp;
        XmlSerializer _xmlSerializer;

        #endregion

        #region Private helpers

        public FileLocalProvider()
        {
            _itcp = ControlPanel.Instance.iTunesControlPanel;
            _xmlSerializer = new XmlSerializer(typeof(LyricsModel));
        }

        #endregion

        #region Own Properties

        public string FolderPath { get; set; }

        #endregion

        #region Private helpers

        #endregion

        #region ILyricsLocalProvider Members

        public string DisplayName
        {
            get { return "File provider to save lyrics in .xml files in a specified folder"; }
        }

        /// <summary>
        /// Gets the lyrics from the xml file or returns null
        /// </summary>
        /// <param name="persistentID"></param>
        /// <returns></returns>
        public LyricsModel GetLyrics(ulong persistentID)
        {
            // hexid
            string hexid = _itcp.GetHexRepresentationFromPersistentID(persistentID);

            if (FolderPath == null)
                throw new Exception("You have to first set the FolderPath.");

            var allFiles = Directory.GetFiles(FolderPath);
            var lyricsFile = allFiles.FirstOrDefault(f => new FileInfo(f).Name.StartsWith(hexid));

            if (lyricsFile == null)
                return null;
            else
            {
                try
                {
                    using (var fileStream = File.OpenRead(lyricsFile))
                    using (var xmlReader = XmlReader.Create(fileStream))
                    {

                        if (_xmlSerializer.CanDeserialize(xmlReader))
                        {
                            var model = (LyricsModel)_xmlSerializer.Deserialize(xmlReader);
                            model.ConvertNToRN();
                            return model;
                        }
                        else
                            return null;

                    }
                }
                catch (Exception e)
                {
                    throw new ApplicationException(string.Format(Strings.FileLocalProviderXmlException, e));
                }
            }

        }

        /// <summary>
        /// Saves the lyrics to the xml file
        /// </summary>
        /// <param name="model"></param>
        /// <param name="force"></param>
        /// <returns></returns>
        public bool SaveLyrics(LyricsModel model, bool force)
        {
            // hexid
            string hexid = _itcp.GetHexRepresentationFromPersistentID(model.iTunesPersistentID);

            if (FolderPath == null)
                throw new Exception("You have to first set the FolderPath.");

            try
            {
                var allFiles = Directory.GetFiles(FolderPath);

                var exists = allFiles.Any(f => new FileInfo(f).Name.StartsWith(hexid));

                if (force || (!force && !exists))
                {
                    // delete the file if it exists
                    if (exists)
                        File.Delete(allFiles.First(f => new FileInfo(f).Name.StartsWith(hexid)));

                    using (var fileStream = File.Create(Path.Combine(FolderPath, string.Format("{0}. {1} - {2}.xml", hexid,
                        EncodeFileName(model.Artist), EncodeFileName(model.Name)))))
                    {
                        _xmlSerializer.Serialize(fileStream, model);
                        return true;
                    }
                }
                else
                    return false;

            }
            catch (Exception e)
            {
                throw new ApplicationException(string.Format(Strings.FileLocalProviderXmlException, e));
            }
        }

        /// <summary>
        /// Removes the xml file
        /// </summary>
        /// <param name="persistentID"></param>
        /// <returns></returns>
        public bool RemoveLyrics(ulong persistentID)
        {
            string hexid = _itcp.GetHexRepresentationFromPersistentID(persistentID);

            try
            {
                var allFiles = Directory.GetFiles(FolderPath);

                var exists = allFiles.Any(f => new FileInfo(f).Name.StartsWith(hexid));

                // delete the file if it exists
                if (exists)
                {
                    File.Delete(allFiles.First(f => new FileInfo(f).Name.StartsWith(hexid)));
                    return true;
                }
                else
                    return false;
            }
            catch (Exception e)
            {
                throw new ApplicationException(string.Format(Strings.FileLocalProviderXmlException, e));
            }
        }

        /// <summary>
        /// Checks if the xml file exists
        /// </summary>
        /// <param name="persistentID"></param>
        /// <returns></returns>
        public bool LyricsExists(ulong persistentID)
        {
            // hexid
            string hexid = _itcp.GetHexRepresentationFromPersistentID(persistentID);

            if (FolderPath == null)
                throw new Exception("You have to first set the FolderPath.");

            try
            {
                var allFiles = Directory.GetFiles(FolderPath);

                return allFiles.Any(f => new FileInfo(f).Name.StartsWith(hexid));
            }
            catch (Exception e)
            {
                throw new ApplicationException(string.Format(Strings.FileLocalProviderXmlException, e));
            }

        }

        public int Metric
        {
            get { return 30; }
        }

        public bool IsValid
        {
            get { return Directory.Exists(FolderPath); }
        }

        public bool IsActivated { get; set; }

        #endregion

        #region Private methods

        /// <summary>
        /// Removes invalid characters from filename
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        string EncodeFileName(string fileName)
        {
            char[] invalidChars = Path.GetInvalidFileNameChars();
            foreach (char c in invalidChars)
                fileName = fileName.Replace(c, '_');

            return fileName;
        }

        /// <summary>
        /// Removes invalid characters from folder path
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        string EncodePath(string path)
        {
            List<char> invalidChars = Path.GetInvalidPathChars().ToList();
            invalidChars.AddRange(new char[] { ':', '/', '\\', '*', '?', '"', '<', '>', '|' });

            foreach (char c in invalidChars)
                path = path.Replace(c, '_');

            return path;
        }

        #endregion
    }
}
